﻿
// opencv_demo1.cpp: 定义应用程序的类行为。
//

#include "pch.h"
#include "watermask_cv_dct.h"

#if 1
watermask_cv_dct::watermask_cv_dct()
{
	
}
void watermask_cv_dct::loadWatermask()
{
	if (m_watermask.empty()) {
		m_watermask = getInformationOfBinaryGraph("C:/Users/hanbo/Desktop/test/cs.bmp");
	}
}
Mat watermask_cv_dct::addImageWatermarkWithText(Mat image)
{
	return addImageWatermarkWithText(image, m_watermask, (int)m_watermask.size(),m_deep);
}
/**
	 * 嵌入水印信息
	 * @param image：原图
	 * @param watermark：水印信息,为信息数组
	 * @param p：嵌入强度
	 */
 Mat watermask_cv_dct::addImageWatermarkWithText(Mat image, std::vector<int> &watermark,int waterMarkLen, double p)
{
	std::vector<Mat> allPlanes;

	Mat yuvMat(image.size(),CV_8UC3);
	cvtColor(image, yuvMat, CV_BGR2YUV_I420);
	split(yuvMat, allPlanes);
	//获取YMat矩阵
	Mat YMat = allPlanes.at(0);
	Mat YMat2(YMat.size(),CV_8UC1);

	YMat = addImageWatermarY(YMat, watermark, waterMarkLen, p);

	merge(allPlanes, yuvMat);
	Mat newMat;
	cvtColor(yuvMat, newMat, CV_YUV2BGR_I420);
	return newMat;
}

Mat watermask_cv_dct::addImageWatermarY(Mat imageY)
{
	return addImageWatermarY(imageY, m_watermask, (int)m_watermask.size(), m_deep);
}

//宏块信息
struct watermask_cv_blockInfo
{
	int index;
	double dctval;
	bool operator < (watermask_cv_blockInfo &b)
	{
		return this->dctval < b.dctval;
	}

	/*bool operator > (watermask_cv_blockInfo &b)
	{
		return this->age > b.age;
	}*/

};
Mat watermask_cv_dct::addImageWatermarY(Mat imageY, std::vector<int>& watermark, int waterMarkLen, double p)
 {
	int blocktotal = (imageY.rows / m_blockLen) * (imageY.cols / m_blockLen);
	 if (blocktotal < waterMarkLen) {
		 ASSERT(0);
		 return Mat(); //返回空图
	 }

	 //测试将水印记录到比较暗的宏块上，但测试效果不好，且修改后无法重新找到添加信息的宏块 =========开始
	 //提取宏块，按照亮度排序
	 std::list<watermask_cv_blockInfo>  dctvec;
	 for (int i = 0; i < blocktotal; i++) {
		 //block 表示分块 而且为 方阵
		 //提取每个分块
		 Mat block = getBlock(imageY, i, m_blockLen);
		 Mat DCTY(block.size(), CV_64FC1);
		 //对分块进行DCT变换
		 dct(Mat_<double>(block), DCTY);

		 watermask_cv_blockInfo binfo;
		 binfo.dctval = DCTY.at<double>(0, 0);
		 binfo.index = i;
		 dctvec.push_back(binfo);
		 
	 }
	 dctvec.sort(); //排序，坐标（0，0）表示亮度，越大越亮

	 printf("\n========================================================================");
	 int printfcoutn = 0;
	 for (auto dctvecIter = dctvec.begin(); dctvecIter != dctvec.end() && printfcoutn < 20; dctvecIter++, printfcoutn++) {
		 printf("\nindex=%d,dct:%f", dctvecIter->index, dctvecIter->dctval);
	 }
	 auto dctvectIter = dctvec.begin();
	 //测试代码结束。后面相关变量需要修改========结束

	 //分成4096块
	 for (int i = 0; i < waterMarkLen; i++) {
		 //block 表示分块 而且为 方阵
		 //提取每个分块
		 //Mat block = getBlock(imageY, i, m_blockLen);
		 Mat block = getBlock(imageY, dctvectIter->index, m_blockLen);
		 
		 //int x1 = 1, y1 = 2;
		 //int x2 = 2, y2 = 1;
		 Mat DCTY(block.size(), CV_64FC1);
		 //对分块进行DCT变换
		 dct(Mat_<double>(block), DCTY);

		// DCTY.at<double>(0, 0) = 0;  //test 测试

		 //添加水印方法1
		 /*if (watermark[i] == 1) {
			 DCTY.at<double>(m_x, m_y) = p;
			 DCTY.at<double>(m_x2, m_y2) = 0;
		 }
		 else {
			 DCTY.at<double>(m_x, m_y) = 0;
			 DCTY.at<double>(m_x2, m_y2) = p;
		 }*/

		 //添加水印方法2
		 double r1 = DCTY.at<double>(m_x, m_y);
		 double r2 = DCTY.at<double>(m_x2, m_y2);
		 double detat = abs(r1 - r2); //水印深度
		 if (detat < m_deep) {
			 detat = m_deep;
		 }
		 if (watermark[i] == 1) {  //1白色, 指纹主体, 用r1 > r2来记录
			 if (r1 <= r2) {
				 DCTY.at<double>(m_x, m_y) += detat;
			 }
		 }
		 else {
			 if (r1 >= r2) {   //0黑色, 用r1 < r2来记录
				 DCTY.at<double>(m_x2, m_y2) += detat;
			 }
		 }

		 //对上面分块进行IDCT变换
		 idct(DCTY, DCTY);

		 //保存
		 saveBlock(imageY, DCTY, dctvectIter->index, m_blockLen);

		 dctvectIter++;
	 }
	 return imageY;
 }

/**
 * 提取水印信息
 * @param image：带提取的图片
 * @return int[][]
 */
 Mat watermask_cv_dct::getImageWatermarkWithText(Mat image)
{
	 int blocktotal = (image.rows / m_blockLen) * (image.cols / m_blockLen);
	 int waterMarkLen = water_width * water_height;
	 if (blocktotal < waterMarkLen) {
		 //ASSERT(0);
		 printf("getImageWatermarY error: watermask len big long");
		 return Mat(); //返回空图
	 }

	std::vector<Mat> allPlanes;
	Mat Ycbcr(image.rows, image.cols, CV_8UC1);
	cvtColor(image, Ycbcr, CV_BGR2YUV_I420);
	split(image, allPlanes);

	Mat YMat = allPlanes.at(0);
	std::vector<int> watermark(waterMarkLen);

	//查找嵌入点
	std::list<watermask_cv_blockInfo>  dctvec;
	for (int i = 0; i < blocktotal; i++) {
		//block 表示分块 而且为 方阵
		//提取每个分块
		Mat block = getBlock(YMat, i, m_blockLen);
		Mat DCTY(block.size(), CV_64FC1);
		//对分块进行DCT变换
		dct(Mat_<double>(block), DCTY);

		watermask_cv_blockInfo binfo;
		binfo.dctval = DCTY.at<double>(0, 0);
		binfo.index = i;
		dctvec.push_back(binfo);

	}
	dctvec.sort();
	printf("\n========================================================================");
	int printfcoutn = 0;
	for (auto dctvecIter = dctvec.begin(); dctvecIter != dctvec.end()&& printfcoutn < 20; dctvecIter++, printfcoutn++) {
		printf("\nindex=%d,dct:%f", dctvecIter->index, dctvecIter->dctval);
	}
	auto dctvectIter = dctvec.begin();

	//分成64块，提取每块嵌入的水印信息
	for (int i = 0; i < waterMarkLen; i++) {
		//block 表示分块 而且为 方阵
		//提取每个分块
		Mat block = getBlock(YMat, dctvectIter->index, m_blockLen);
		dctvectIter++;

		//对分块进行DCT变换
		Mat DCTY(block.size(), CV_64FC1);
		dct(Mat_<double>(block), DCTY);
		//用于容纳DCT系数
		//int x1 = 1, y1 = 2;
		//int x2 = 2, y2 = 1;

		double a = DCTY.at<double>(m_x, m_y);
		double c = DCTY.at<double>(m_x2, m_y2);

		if (a >= c) {
			watermark[i] = 1;
		}else {
			watermark[i] = 0;
		}
	}

	//保存水印为Mat
	Mat binaryPhoto(water_height,water_width, THRESH_BINARY);
	int index = 0;
	for (int i = 0; i < water_height; i++) {
		for (int j = 0; j < water_width; j++) {
			if (watermark[index] == 1) {
				binaryPhoto.at<uchar>(i, j) = 255;
			}
			else {
				binaryPhoto.at<uchar>(i, j) = 0;
			}
			index++;
		}
	}
	return binaryPhoto;
}

/**
 * 提取每个分块。将原图按照length，分成多个分块
 * @param YMat：原分块
 * @param x：第几个块
 * @param length：每个块的长度（正方形）
 * @return
 */
Mat watermask_cv_dct::getBlock(Mat YMat, int index, int blockLen) {
	Mat mat(blockLen, blockLen, CV_8UC1);

	int rowBlockCount = YMat.cols / blockLen;  //一行的最大块数

	int x = index / rowBlockCount;   //行
	int y = index % rowBlockCount;   //列

	for (int i= 0; i < blockLen; i++) {
		for (int j= 0; j < blockLen; j++) {
			mat.at<uchar>(i, j) = YMat.at<uchar>(x * blockLen + i, y * blockLen + j);
		}
	}
	return mat;
}
void watermask_cv_dct::saveBlock(Mat YMat, Mat& dctBlockMat, int index, int blockLen) {
	int rowBlockCount = YMat.cols / blockLen;  //一行的最大块数

	int x = index / rowBlockCount;   //行
	int y = index % rowBlockCount;   //列

	for (int i = 0; i < blockLen; i++) {
		for (int j = 0; j < blockLen; j++) {
			YMat.at<uchar>(x * blockLen + i, y * blockLen + j) = dctBlockMat.at<double>(i, j);
		}
	}
}

/**
 * 获取二值图的信息
 */
std::vector<int> watermask_cv_dct::getInformationOfBinaryGraph(String srcPath)
{
	std::vector<int>  info; 

	int width = water_width;
	int height = water_height;

	Mat mat = imread(srcPath);
	resize(mat, mat,Size(width, height));
	cvtColor(mat, mat, CV_BGR2GRAY);
	cv::threshold(mat, mat, 128, 255, THRESH_BINARY); //黑白图
	//imshow("watermask_orig", mat);
	
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			if (mat.at<uchar>(i, j) == 255)
				info.push_back(1);
			else
				info.push_back(0);
		}
	}
	return info;
}



/**
 * 将一张图片压缩成一张64x64的二值图
 * @param srcPath
 * @param dstPath
 */
#if 0
public static String getBinaryPhoto(String srcPath, String dstPath) {

	srcPath = thumbnail(srcPath, dstPath, 64, 64);

	//得到原图
	File file = new File(srcPath);
	BufferedImage image = null;
	try {
		image = ImageIO.read(file);
	}
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	int width = image.getWidth();
	int height = image.getHeight();
	//创建原图的二值图
	BufferedImage binaryPhoto = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
	int min = new Color(0, 0, 0).getRGB();
	int max = new Color(255, 255, 255).getRGB();
	//判断标记
	int flag = 170;
	for (int i = 0; i < width; i++) {
		for (int j = 0; j < height; j++) {
			//像素
			int pixel = image.getRGB(i, j);
			//得到 rgb通道对应的元素
			int r, g, b;
			r = (pixel & 0xff0000) >> 16;
			g = (pixel & 0xff00) >> 8;
			b = (pixel & 0xff);
			int avg = (r + g + b) / 3;
			if (avg <= flag)
				binaryPhoto.setRGB(i, j, min);
			else
				binaryPhoto.setRGB(i, j, max);
		}
	}
	try {
		ImageIO.write(binaryPhoto, "bmp", new File(dstPath));
	}
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return dstPath;
}



/**
 * 将图片变成指定大小的图片
 */
public static String thumbnail(String srcImagePath, String desImagePath, int w, int h) {

	Mat src = Imgcodecs.imread(srcImagePath);
	Mat dst = src.clone();
	Imgproc.resize(src, dst, new Size(w, h));
	Imgcodecs.imwrite(desImagePath, dst);
	return desImagePath;
}
#endif

void watermask_cv_dct::myPrintCurTime(const char *pstr)
{
	SYSTEMTIME st;
	GetLocalTime(&st);
	printf("\n%s,time: %u:%d", pstr, st.wSecond, st.wMilliseconds);
}

#endif